package org.colafries.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.colafries.admin.entity.domain.CustomTable;
import org.colafries.admin.entity.domain.DataModel;
import org.colafries.admin.entity.domain.DataModelConfig;
import org.colafries.admin.entity.vo.*;
import org.colafries.admin.exceptions.ServiceException;
import org.colafries.admin.exceptions.enums.ErrorCodeEnum;
import org.colafries.admin.mapper.DataModelConfigMapper;
import org.colafries.admin.mapper.DataModelMapper;
import org.colafries.admin.service.ICustomTableService;
import org.colafries.admin.service.IDataBaseService;
import org.colafries.admin.service.IDataModelConfigService;
import org.colafries.admin.service.IDataModelService;
import org.colafries.common.enums.ColumnTypeEnum;
import org.colafries.common.enums.Flag;
import org.colafries.common.metadata.Column;
import org.colafries.common.metadata.Schema;
import org.colafries.common.snowflake.SnowFlake;
import org.colafries.common.utils.CommonUtil;
import org.colafries.common.utils.HashUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author colafries
 * @version v1.0
 * @ClassName ModelServiceImpl
 * @Description
 * @date Created in 2024/3/14
 **/
@Service
@Slf4j
public class IDataModelServiceImpl extends ServiceImpl<DataModelMapper, DataModel> implements IDataModelService {

    @Resource
    DataModelMapper dataModelMapper;
    @Resource
    DataModelConfigMapper dataModelConfigMapper;
    @Resource
    IDataModelConfigService iDataModelConfigService;
    @Resource
    IDataBaseService iDataBaseService;
    @Resource
    ICustomTableService iCustomTableService;

    /**
     * 查找全部模型
     *
     * @param vo
     * @return java.util.List<org..colafries.admin.entity.domain.DataModel>
     * @author colafries
     * @Date 2024/4/18
     */
    @Override
    public List<DataModel> getAll(DataModelReqVO vo) {
        LambdaQueryWrapper<DataModel> queryWrapper = new LambdaQueryWrapper<>();
        if (!Strings.isNullOrEmpty(vo.getModelName())) {
            queryWrapper.like(DataModel::getModelName, vo.getModelName());
        }
        // TODO 其他筛选条件 ... ...

        return dataModelMapper.selectList(queryWrapper);
    }

    /**
     * 分页查找模型
     *
     * @param vo
     * @return com.baomidou.mybatisplus.core.metadata.IPage<org..colafries.admin.entity.domain.DataModel>
     * @author colafries
     * @Date 2024/4/18
     */
    @Override
    public IPage<DataModel> getPageList(DataModelReqVO vo) {
        LambdaQueryWrapper<DataModel> queryWrapper = new LambdaQueryWrapper<>();
        if (!Strings.isNullOrEmpty(vo.getModelName())) {
            queryWrapper.like(DataModel::getModelName, vo.getModelName());
        }
        // TODO 其他筛选条件 ... ...
        Page<DataModel> page = new Page<>(vo.getCurrent(), vo.getSize());
        IPage<DataModel> result = dataModelMapper.selectPage(page, queryWrapper);

        if (result.getTotal() > 0 && result.getRecords().isEmpty()) {
            long current = result.getPages();
            page = new Page<>((int) current, vo.getSize());
            result = dataModelMapper.selectPage(page, queryWrapper);
        }
        return result;
    }

    /**
     * 根据模型id查找模型
     *
     * @param modelId
     * @return org..colafries.admin.entity.domain.DataModel
     * @author colafries
     * @Date 2024/4/18
     */
    @Override
    public DataModel getModelById(Integer modelId) {
        if (null == modelId) {
            throw new ServiceException("模型id为空");
        }
        DataModel dataModel = dataModelMapper.selectById(modelId);
        if (null == dataModel) {
            throw new ServiceException("数据模型不存在 模型id " + modelId);
        }
        return dataModel;
    }

    /**
     * 新增或修改模型
     *
     * @param dataModelReqVO
     * @return void
     * @author colafries
     * @Date 2024/4/18
     */
    @Override
    @Transactional
    public void saveOrUpdate(DataModelReqVO dataModelReqVO) {
        try {
            // 1.新增or修改模型
            DataModel dataModel = buildDataModel(dataModelReqVO);
            this.saveOrUpdate(dataModel);
            // 2.自定义表
            CustomTable customTable = CustomTable.builder()
                    .code(Long.toString(SnowFlake.nextId()))
                    .customSql(dataModelReqVO.getCustomSql())
                    //customTable name 暂时使用模型的名称
                    .name(dataModelReqVO.getModelName())
                    .tableHash(dataModelReqVO.genCustomTableHash())
                    .dataModelId(dataModel.getId())
                    .updateUser(dataModelReqVO.getUpdateUser())
                    .build();
            iCustomTableService.remove(Wrappers.<CustomTable>lambdaUpdate().eq(CustomTable::getDataModelId, dataModel.getId()));
            iCustomTableService.save(customTable);
            // 3.数据模型配置
            List<DataModelConfig> dataModelConfigs = dataModelConfigMapper.selectList(
                    Wrappers.<DataModelConfig>lambdaQuery().eq(DataModelConfig::getDataModelId, dataModel.getId()));
            if (CollectionUtil.isNotEmpty(dataModelConfigs)) {
                List<Integer> ids = dataModelConfigs.stream().map(DataModelConfig::getId).collect(Collectors.toList());
                iDataModelConfigService.removeByIds(ids);
            }
            List<DataModelConfig> configs = buildConfigList(dataModelReqVO, dataModel.getId(), customTable.getId());
            iDataModelConfigService.saveBatch(configs);
        } catch (ServiceException e) {
            log.error("保存更新模型失败 {}", e.getMessage());
            throw new ServiceException(e.getMessage());
        } catch (Exception e) {
            log.error("保存更新模型失败 {}", e.getMessage());
            throw new ServiceException("保存更新模型失败 " + e.getMessage());
        }
    }

    private DataModel buildDataModel(DataModelReqVO dataModelReqVO) {
        DataModel dataModel = new DataModel();
        if (dataModelReqVO.getModelId() != null) {
            dataModel.setId(dataModelReqVO.getModelId());
        }
        if (!Strings.isNullOrEmpty(dataModelReqVO.getCode())) {
            dataModel.setCode(dataModelReqVO.getCode());
        } else {
            dataModel.setCode(Long.toString(SnowFlake.nextId()));
        }
        dataModel.setModelName(dataModelReqVO.getModelName());
        dataModel.setDatabaseId(dataModelReqVO.getDatabaseId());
        dataModel.setModelDesc(dataModelReqVO.getModelDesc());
//        dataModel.setModelType(modelReqVO.getDataModelType());
        dataModel.setModelLabel(dataModelReqVO.getModelLabel());
        dataModel.setUpdateUser(dataModelReqVO.getUpdateUser());
        return dataModel;
    }

    /**
     * 构建模型配置List
     *
     * @param vo      ModelReqVO
     * @param modelId 数据模型id
     * @return java.util.List<org..colafries.admin.entity.domain.DataModelConfig>
     * @author colafries
     * @Date 2024/3/22
     */
    public List<DataModelConfig> buildConfigList(DataModelReqVO vo, Integer modelId, Integer customTableId) {
        DataModelReqVO.DataModelConfigDTO configVo = vo.getDataModelConfig();
        if (null == configVo) {
            throw new RuntimeException("模型配置不能为空");
        }
        // 维度、度量
        List<DataModelReqVO.Dimension> dimensions = configVo.getDimensions();
        List<DataModelReqVO.Measure> measures = configVo.getMeasures();

        //创建空List
        List<DataModelConfig> configs = CollectionUtil.newArrayList();

        // 添加维度
        if (CollectionUtil.isNotEmpty(dimensions)) {
            configs.addAll(
                    dimensions.stream().map(dimension -> {
                        DataModelConfig dataModelConfig = new DataModelConfig();
                        dataModelConfig.setCode(Long.toString(SnowFlake.nextId()))
                                .setDataModelId(modelId)
                                .setTableId(customTableId)
                                .setFieldHash(dimension.genFieldHash())
                                .setField(dimension.getField())
                                .setAlias(dimension.getAlias())
                                .setDataType(dimension.getDataType())
                                .setType(ColumnTypeEnum.DIMENSION)
                                .setFilterFlag(dimension.getFilterFlag())
                                //expression
                                .setExpression(dimension.getExpression())
                                //calculated 1 or 0
                                .setCalculatedFlag(dimension.getCalculated());
                        return dataModelConfig;
                    }).collect(Collectors.toList())
            );
        }
        // 添加度量
        if (CollectionUtil.isNotEmpty(measures)) {
            measures.stream().map(measure -> {
                DataModelConfig dataModelConfig = new DataModelConfig();
                dataModelConfig.setCode(Long.toString(SnowFlake.nextId()))
                        .setDataModelId(modelId)
                        .setTableId(customTableId)
                        .setFieldHash(measure.genFieldHash())
                        .setField(measure.getField())
                        .setAlias(measure.getAlias())
                        .setDefaultAggregator(measure.getDefaultAggregator())
                        .setDataType(measure.getDataType())
                        .setType(ColumnTypeEnum.MEASURE)
                        .setFilterFlag(measure.getFilterFlag())
                        //expression
                        .setExpression(measure.getExpression())
                        //calculated 1 or 0
                        .setCalculatedFlag(measure.getCalculated())
                        // data format
                        .setAccuracy(measure.getFormat().getAccuracy())
                        .setDataFormat(measure.getFormat().getDataFormat())
                        .setUnit(measure.getFormat().getUnit());
                return dataModelConfig;
            }).forEach(configs::add);
        }
        return configs;
    }

    /**
     * 根据id删除模型
     *
     * @param vo
     * @return int
     * @author colafries
     * @Date 2024/4/18
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByIds(DataModelReqVO vo) {
        int deleteCnt = 0;
        for (Integer id : vo.getIds()) {
            // 1.dataModel delete
            deleteCnt += dataModelMapper.deleteById(id);

            // 2.custom table delete 按照modelId 删除 CustomTable TODO 后续调整
            List<CustomTable> customTables = iCustomTableService.lambdaQuery().eq(CustomTable::getDataModelId, id).list();
            if (CollectionUtil.isNotEmpty(customTables)) {
                // 按照customTableId 进行删除
                iCustomTableService.removeByIds(
                        customTables.stream().map(CustomTable::getId).collect(Collectors.toList())
                );
            }

            // 3.dataModel config delete
            List<DataModelConfig> dataModelConfigs = iDataModelConfigService.lambdaQuery().eq(DataModelConfig::getDataModelId, id).list();
            if (CollectionUtil.isNotEmpty(dataModelConfigs)) {
                iDataModelConfigService.removeByIds(
                        dataModelConfigs.stream().map(DataModelConfig::getId).collect(Collectors.toList())
                );
            }
        }
        return deleteCnt;
    }

    /**
     * 通过modelId构建ModelReqVO
     *
     * @param dataModelReqVO
     * @return void
     * @Author anning
     * @Date 2024/9/9
     */
    private void buildModelReqVOById(DataModelReqVO dataModelReqVO) {
        try {
            DataModel oldDataModel = getModelById(dataModelReqVO.getModelId());
            if (oldDataModel == null) {
                log.warn("数据模型不存在，模型id {}", dataModelReqVO.getModelId());
                throw new ServiceException("数据模型不存在");
            }
            List<CustomTable> customTables = iCustomTableService.lambdaQuery()
                    .eq(CustomTable::getDataModelId, dataModelReqVO.getModelId())
                    .list();
            if (CollectionUtil.isEmpty(customTables)) {
                log.warn("模型自定义表不存在，模型id {}", dataModelReqVO.getModelId());
                throw new ServiceException("模型自定义表不存在");
            }
            dataModelReqVO.setModelId(oldDataModel.getId());
            dataModelReqVO.setCode(oldDataModel.getCode());
            dataModelReqVO.setModelName(oldDataModel.getModelName());
            dataModelReqVO.setCustomSql(customTables.get(0).getCustomSql());
            dataModelReqVO.setModelLabel(oldDataModel.getModelLabel());
            dataModelReqVO.setModelDesc(oldDataModel.getModelDesc());
            dataModelReqVO.setUpdateUser(oldDataModel.getUpdateUser());
            dataModelReqVO.setDatabaseId(oldDataModel.getDatabaseId());
            List<DataModelConfig> configs = iDataModelConfigService.lambdaQuery()
                    .eq(DataModelConfig::getDataModelId, oldDataModel.getId())
                    .list();
            DataModelReqVO.DataModelConfigDTO configDTO = new DataModelReqVO.DataModelConfigDTO();
            List<DataModelReqVO.Dimension> dimensions = Lists.newArrayList();
            List<DataModelReqVO.Measure> measures = Lists.newArrayList();
            List<CustomQueryReqVO.Fuzzy> fuzzyList = Lists.newArrayList();
            configs.forEach(config -> {
                if (config.getType().equals(ColumnTypeEnum.DIMENSION)) {
                    DataModelReqVO.Dimension dimension = new DataModelReqVO.Dimension();
                    dimension.setId(config.getId());
                    dimension.setTableId(config.getTableId());
                    dimension.setFieldHash(config.getFieldHash());
                    dimension.setField(config.getField());
                    dimension.setAlias(config.getAlias());
                    dimension.setExpression(config.getExpression());
                    dimension.setDataType(config.getDataType());
                    dimension.setFilterFlag(config.getFilterFlag());
                    dimension.setCalculated(config.getCalculatedFlag());
                    dimensions.add(dimension);
                }
                if (config.getType().equals(ColumnTypeEnum.MEASURE)) {
                    DataModelReqVO.Measure measure = new DataModelReqVO.Measure();
                    measure.setId(config.getId());
                    measure.setId(config.getId());
                    measure.setTableId(config.getTableId());
                    measure.setFieldHash(config.getFieldHash());
                    measure.setField(config.getField());
                    measure.setAlias(config.getAlias());
                    measure.setDefaultAggregator(config.getDefaultAggregator());
                    measure.setExpression(config.getExpression());
                    measure.setFormat(DataModelReqVO.Format.builder()
                            .accuracy(config.getAccuracy()).dataFormat(config.getDataFormat()).unit(config.getUnit())
                            .build());
                    measure.setDataType(config.getDataType());
                    measure.setFilterFlag(config.getFilterFlag());
                    measure.setCalculated(config.getCalculatedFlag());
                    measures.add(measure);
                }
            });
            configDTO.setDimensions(dimensions);
            configDTO.setMeasures(measures);
            configDTO.setFuzzyList(fuzzyList);

            dataModelReqVO.setDataModelConfig(configDTO);
//            modelReqVO.setDataModelType(oldDataModel.getModelType());
        } catch (ServiceException e) {
            log.warn("");
            throw new ServiceException(e.getMessage());
        } catch (Exception e) {
            log.error("");
        }
    }

    /**
     * 复制模型
     *
     * @param dataModelReqVO
     * @return int
     * @author colafries
     * @Date 2024/4/18
     */
    @Override
    public void copyModel(DataModelReqVO dataModelReqVO) {
        buildModelReqVOById(dataModelReqVO);

        // 复制模型，相当于新建一个新模型，modelId置为空
        dataModelReqVO.setModelId(null);
        dataModelReqVO.setCode(null);
        Flag flag = Flag.YES;
        String newModelName = "";
        while (flag.equals(Flag.YES)) {
            newModelName = dataModelReqVO.getModelName() + "的副本_" + CommonUtil.generateUniqueIdentifier();
            List<DataModel> dataModelList = dataModelMapper.selectList(Wrappers.<DataModel>lambdaQuery().eq(DataModel::getModelName, newModelName));
            if (CollectionUtil.isNotEmpty(dataModelList)) {
                return;
            } else {
                flag = Flag.NO;
            }
        }
        // 复制模型，分配新的模型名称
        dataModelReqVO.setModelName(newModelName);
        this.saveOrUpdate(dataModelReqVO);

    }


    @Override
    public void exportModel(DataModelReqVO dataModelReqVO) {
        buildModelReqVOById(dataModelReqVO);
    }

    @Override
    public void importModel(DataModelReqVO dataModelReqVO) {
        // 判断是否存在code相同模型
        DataModel dataModel = this.getOne(Wrappers.<DataModel>lambdaQuery().eq(DataModel::getCode, dataModelReqVO.getCode()));
        if (dataModel != null) {
            throw new ServiceException("数据模型code已存在 code " + dataModelReqVO.getCode());
        }
        this.saveOrUpdate(dataModelReqVO);
    }


    /**
     * 查询数据模型配置
     *
     * @param modelConfigReqVO
     * @return org..colafries.admin.entity.domain.DataModelConfig
     * @author colafries
     * @Date 2024/3/26
     */
    @Override
    public List<DataModelConfigRspVO> getModelConfig(DataModelConfigReqVO modelConfigReqVO) {
        List<DataModelConfig> dataModelConfigs = iDataModelConfigService.queryModelConfig(modelConfigReqVO);
        // 如果模型配置不存在
        if (CollectionUtil.isEmpty(dataModelConfigs)) {
            return Collections.emptyList();
        }
        List<DataModelConfigRspVO> dataModelConfigRspVOList = CollectionUtil.newArrayList();
        dataModelConfigs.forEach(dataModelConfig -> {
            DataModelConfigRspVO dataModelConfigRspVO = new DataModelConfigRspVO();
            BeanUtils.copyProperties(dataModelConfig, dataModelConfigRspVO);
            dataModelConfigRspVOList.add(dataModelConfigRspVO);
        });
        return dataModelConfigRspVOList;
    }


    /**
     * 获取表结构
     *
     * @param customTableReqVO
     * @return java.util.List<org..colafries.admin.entity.vo.ColumnRspVO>
     * @author colafries
     * @Date 2024/4/18
     */
    @Override
    public List<ColumnRspVO> getTableSchema(CustomTableReqVO customTableReqVO) {
        String tableName = customTableReqVO.getTableName();
        Integer modelId = customTableReqVO.getDataModelId();

        DataModel dataModel = this.getModelById(modelId);
        Integer dataSourceId = dataModel.getDatabaseId();
        customTableReqVO.setDataSourceId(dataSourceId);

        // 1.查询自定义表
        List<ColumnRspVO> customTableColumns = iCustomTableService.getTableSchema(customTableReqVO);
        if (!CollectionUtil.isEmpty(customTableColumns)) {
            return customTableColumns;
        }

        //2.自定义表不存在，查询物理表
        List<Schema> schemaList = iDataBaseService.getSchemasAndTables(new DataBaseReqVO().setDatabaseId(dataSourceId));
        String schemaName = schemaList.stream()
                .filter(schema -> schema.containsTable(tableName))
                .findFirst()
                .map(Schema::getSchemaName)
                .orElseThrow(() -> new ServiceException(ErrorCodeEnum.GL999999, tableName + "表不存在"));
        //
        List<Column> columnList = iDataBaseService.getTableSchema(
                new DataBaseReqVO().setDatabaseId(dataSourceId).setSchemaName(schemaName).setTableName(tableName)
        );

        return columnList.stream()
                .map(column -> ColumnRspVO.builder()
                        .field(column.getName())
                        .dbType(column.getType())
                        .javaType(column.getJavaTypeFormat())
                        .comment(column.getComment())
                        .fieldHash(HashUtil.genFieldHash(tableName + "." + column.getName()))
                        .build())
                .collect(Collectors.toList());
    }

}
